#pragma once

#include "Behavior.h"

#include "BehavioralFeature.h"
#include "BehavioredClassifier.h"
#include "Constraint.h"
#include "Element.h"
#include "Parameter.h"
#include "ParameterSet.h"

namespace uml {

const bool Behavior::isReentrant_default_ = true;
Behavior::Behavior(const QString& umlType)
    : Class(umlType) {}

void Behavior::fromJson(const QJsonObject& json, QStringList& errorList) {
    if (json.isEmpty()) {
        return;
    }

    Class::fromJson(json, errorList);
    context_ = JsonSerializable::create<BehavioredClassifier>(json.value("context"), errorList);

    isReentrant_ = json.value("isReentrant").toBool(isReentrant_default_);
    JsonSerializable::fromJsonArray(ownedParameters_, "ownedParameters", json, errorList);
    JsonSerializable::fromJsonArray(ownedParameterSets_, "ownedParameterSets", json, errorList);
    JsonSerializable::fromJsonArray(postconditions_, "postconditions", json, errorList);
    JsonSerializable::fromJsonArray(preconditions_, "preconditions", json, errorList);
    specification_ = JsonSerializable::create<BehavioralFeature>(json.value("specification"), errorList);

    JsonSerializable::fromJsonArray(redefinedBehaviors_, "redefinedBehaviors", json, errorList);
}

void Behavior::toJson(QJsonObject& json) {
    Class::toJson(json);
    if (context_) {
        QJsonObject context_json;
        context_->toJson(context_json);
        json.insert("context", context_json);
    }
    if (isReentrant_ != isReentrant_default_) {
        json.insert("isReentrant", isReentrant_);
    }
    JsonSerializable::toJsonArray(ownedParameters_, "ownedParameters", json);
    JsonSerializable::toJsonArray(ownedParameterSets_, "ownedParameterSets", json);
    JsonSerializable::toJsonArray(postconditions_, "postconditions", json);
    JsonSerializable::toJsonArray(preconditions_, "preconditions", json);
    if (specification_) {
        QJsonObject specification_json;
        specification_->toJson(specification_json);
        json.insert("specification", specification_json);
    }
    JsonSerializable::toJsonArray(redefinedBehaviors_, "redefinedBehaviors", json);
}

/// <summary>
/// behavioredClassifier
    /// The first BehavioredClassifier reached by following the chain of owner
    /// relationships from the Behavior, if any.
/// </summary>
    /// <code>
    /// if from.oclIsKindOf(BehavioredClassifier) then
    ///     from.oclAsType(BehavioredClassifier)
    /// else if from.owner = null then
    ///     null
    /// else
    ///     self.behavioredClassifier(from.owner)
    /// endif
    /// endif
    ///     
    /// </code>
shared_ptr<BehavioredClassifier> Behavior::behavioredClassifier(shared_ptr<Element> from) const
{
    return nullptr;
}

/// <summary>
/// inputParameters
    /// The in and inout ownedParameters of the Behavior.
/// </summary>
    /// <code>
    /// result = (ownedParameter->select(direction=ParameterDirectionKind::_'in' or direction=ParameterDirectionKind::inout))
    /// </code>
shared_ptr<vector<Parameter>> Behavior::inputParameters() const
{
    return nullptr;
}

/// <summary>
/// outputParameters
    /// The out, inout and return ownedParameters.
/// </summary>
    /// <code>
    /// result = (ownedParameter->select(direction=ParameterDirectionKind::out or direction=ParameterDirectionKind::inout or direction=ParameterDirectionKind::return))
    /// </code>
shared_ptr<vector<Parameter>> Behavior::outputParameters() const
{
    return nullptr;
}

/// <summary>
/// context getter
/// The BehavioredClassifier that is the context for the execution of the Behavior.
/// A Behavior that is directly owned as a nestedClassifier does not have a context.
/// Otherwise, to determine the context of a Behavior, find the first
/// BehavioredClassifier reached by following the chain of owner relationships from
/// the Behavior, if any. If there is such a BehavioredClassifier, then it is the
/// context, unless it is itself a Behavior with a non-empty context, in which case
/// that is also the context for the original Behavior. For example, following this
/// algorithm, the context of an entry Behavior in a StateMachine is the
/// BehavioredClassifier that owns the StateMachine. The features of the context
/// BehavioredClassifier as well as the Elements visible to the context Classifier
/// are visible to the Behavior.
/// </summary>
shared_ptr<BehavioredClassifier> Behavior::context() const {
    return context_;
}

/// <summary>
/// context setter
/// The BehavioredClassifier that is the context for the execution of the Behavior.
/// A Behavior that is directly owned as a nestedClassifier does not have a context.
/// Otherwise, to determine the context of a Behavior, find the first
/// BehavioredClassifier reached by following the chain of owner relationships from
/// the Behavior, if any. If there is such a BehavioredClassifier, then it is the
/// context, unless it is itself a Behavior with a non-empty context, in which case
/// that is also the context for the original Behavior. For example, following this
/// algorithm, the context of an entry Behavior in a StateMachine is the
/// BehavioredClassifier that owns the StateMachine. The features of the context
/// BehavioredClassifier as well as the Elements visible to the context Classifier
/// are visible to the Behavior.
/// </summary>
void Behavior::setContext(shared_ptr<BehavioredClassifier> value) {
    context_ = value;
}

/// <summary>
/// isReentrant getter
/// Tells whether the Behavior can be invoked while it is still executing from a
/// previous invocation.
/// </summary>
bool Behavior::isReentrant() const {
    return isReentrant_;
}

/// <summary>
/// isReentrant setter
/// Tells whether the Behavior can be invoked while it is still executing from a
/// previous invocation.
/// </summary>
void Behavior::setIsReentrant(bool value) {
    isReentrant_ = value;
}

/// <summary>
/// ownedParameters getter
/// References a list of Parameters to the Behavior which describes the order and
/// type of arguments that can be given when the Behavior is invoked and of the
/// values which will be returned when the Behavior completes its execution.
/// </summary>
vector<shared_ptr<Parameter>>& Behavior::ownedParameters() {
    return ownedParameters_;
}

/// <summary>
/// ownedParameterSets getter
/// The ParameterSets owned by this Behavior.
/// </summary>
vector<shared_ptr<ParameterSet>>& Behavior::ownedParameterSets() {
    return ownedParameterSets_;
}

/// <summary>
/// postconditions getter
/// An optional set of Constraints specifying what is fulfilled after the execution
/// of the Behavior is completed, if its precondition was fulfilled before its
/// invocation.
/// </summary>
vector<shared_ptr<Constraint>>& Behavior::postconditions() {
    return postconditions_;
}

/// <summary>
/// preconditions getter
/// An optional set of Constraints specifying what must be fulfilled before the
/// Behavior is invoked.
/// </summary>
vector<shared_ptr<Constraint>>& Behavior::preconditions() {
    return preconditions_;
}

/// <summary>
/// specification getter
/// Designates a BehavioralFeature that the Behavior implements. The
/// BehavioralFeature must be owned by the BehavioredClassifier that owns the
/// Behavior or be inherited by it. The Parameters of the BehavioralFeature and the
/// implementing Behavior must match. A Behavior does not need to have a
/// specification, in which case it either is the classifierBehavior of a
/// BehavioredClassifier or it can only be invoked by another Behavior of the
/// Classifier.
/// </summary>
shared_ptr<BehavioralFeature> Behavior::specification() const {
    return specification_;
}

/// <summary>
/// specification setter
/// Designates a BehavioralFeature that the Behavior implements. The
/// BehavioralFeature must be owned by the BehavioredClassifier that owns the
/// Behavior or be inherited by it. The Parameters of the BehavioralFeature and the
/// implementing Behavior must match. A Behavior does not need to have a
/// specification, in which case it either is the classifierBehavior of a
/// BehavioredClassifier or it can only be invoked by another Behavior of the
/// Classifier.
/// </summary>
void Behavior::setSpecification(shared_ptr<BehavioralFeature> value) {
    specification_ = value;
}

/// <summary>
/// redefinedBehaviors getter
/// References the Behavior that this Behavior redefines. A subtype of Behavior may
/// redefine any other subtype of Behavior. If the Behavior implements a
/// BehavioralFeature, it replaces the redefined Behavior. If the Behavior is a
/// classifierBehavior, it extends the redefined Behavior.
/// </summary>
vector<shared_ptr<Behavior>>& Behavior::redefinedBehaviors() {
    return redefinedBehaviors_;
}

} // namespace uml
